/*
 * Copyright (c) 2019 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

/** @file
 *  @brief Nordic Battery Service Client sample
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <inttypes.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <bluetooth/gatt_dm.h>
#include <bluetooth/scan.h>
#include <dk_buttons_and_leds.h>
#include <zephyr/settings/settings.h>
#include <zephyr/logging/log.h>
#include "bas_client.h"

LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
/**
 * Button to read the battery value
 */
#define KEY_READVAL_MASK DK_BTN1_MSK

static struct bt_conn *default_conn;
static struct bas_client bas;

/************************************************************************************************************************************************/

static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));

	LOG_INF("Filters matched. Address: %s connectable: %s\n", addr, connectable ? "yes" : "no");
}

static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
	LOG_WRN("Connecting failed\n");
}

static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
	default_conn = bt_conn_ref(conn);
}

static void scan_filter_no_match(struct bt_scan_device_info *device_info, bool connectable)
{
	int err;
	struct bt_conn *conn;
	char addr[BT_ADDR_LE_STR_LEN];

	if (device_info->recv_info->adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND)
	{
		bt_addr_le_to_str(device_info->recv_info->addr, addr,
						  sizeof(addr));
		LOG_INF("Direct advertising received from %s\n", addr);
		bt_scan_stop();

		err = bt_conn_le_create(device_info->recv_info->addr,
								BT_CONN_LE_CREATE_CONN,
								device_info->conn_param, &conn);

		if (!err)
		{
			default_conn = bt_conn_ref(conn);
			bt_conn_unref(conn);
		}
	}
}

/*******************************************************************************
 * @brief 使用宏，定义扫描回调
 * static const struct cb_data scan_cb_data = 
 * { 
 * 	.filter_match = scan_filter_match, 
 * 	.filter_no_match = scan_filter_no_match, 
 * 	.connecting_error = scan_connecting_error, 
 * 	.connecting = scan_connecting, 
 *	}; static struct bt_scan_cb scan_cb = { .cb_addr = &scan_cb_data, }
 * 
*******************************************************************************/
BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match,
				scan_connecting_error, scan_connecting);

/************************************************************************************************************************************************/

static void discovery_completed_cb(struct bt_gatt_dm *dm, void *context)
{
	int err;

	LOG_INF("The discovery procedure succeeded\n");

	bt_gatt_dm_data_print(dm);

	err = bas_handles_assign(dm, &bas);
	if (err)
	{
		LOG_ERR("Could not init BAS client object, error: %d\n", err);
	}

	if (bas_notify_supported(&bas))
	{
		err = bas_subscribe_battery_level(&bas);
		if (err)
		{
			LOG_ERR("Cannot subscribe to BAS value notification (err: %d)\n", err);
			/* Continue anyway */
		}
	}

	err = bt_gatt_dm_data_release(dm);
	if (err)
	{
		LOG_ERR("Could not release the discovery data, error "
				"code: %d\n",
				err);
	}
}

/*******************************************************************************
 * @brief 
 * 
 * @param conn 
 * @param context 
*******************************************************************************/
static void discovery_service_not_found_cb(struct bt_conn *conn,
										   void *context)
{
	LOG_WRN("The service could not be found during the discovery\n");
}

static void discovery_error_found_cb(struct bt_conn *conn,
									 int err,
									 void *context)
{
	LOG_ERR("The discovery procedure failed with %d\n", err);
}

static struct bt_gatt_dm_cb discovery_cb = {
	.completed = discovery_completed_cb,
	.service_not_found = discovery_service_not_found_cb,
	.error_found = discovery_error_found_cb,
};

static void gatt_discover(struct bt_conn *conn)
{
	int err;

	if (conn != default_conn)
	{
		return;
	}

	err = bt_gatt_dm_start(conn, BT_UUID_BAS, &discovery_cb, NULL);
	if (err)
	{
		LOG_ERR("Could not start the discovery procedure, error "
				"code: %d\n",
				err);
	}
}

/*******************************************************************************
 *
 ******************************************************************************/
static void connected(struct bt_conn *conn, uint8_t conn_err)
{
	int err;
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	if (conn_err)
	{
		LOG_ERR("Failed to connect to %s (%u)\n", addr, conn_err);
		if (conn == default_conn)
		{
			bt_conn_unref(default_conn);
			default_conn = NULL;

			/* This demo doesn't require active scan */
			err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
			if (err)
			{
				LOG_ERR("Scanning failed to start (err %d)\n",
						err);
			}
		}

		return;
	}

	LOG_INF("Connected: %s\n", addr);

	err = bt_conn_set_security(conn, BT_SECURITY_L2);
	if (err)
	{
		LOG_WRN("Failed to set security: %d\n", err);

		gatt_discover(conn);
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];
	int err;

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_WRN("Disconnected: %s (reason %u)\n", addr, reason);

	if (default_conn != conn)
	{
		return;
	}

	bt_conn_unref(default_conn);
	default_conn = NULL;

	/* This demo doesn't require active scan */
	err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
	if (err)
	{
		LOG_ERR("Scanning failed to start (err %d)\n", err);
	}
}

static void security_changed(struct bt_conn *conn, bt_security_t level,
							 enum bt_security_err err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	if (!err)
	{
		LOG_INF("Security changed: %s level %u\n", addr, level);
	}
	else
	{
		LOG_WRN("Security failed: %s level %u err %d\n", addr, level,
				err);
	}

	gatt_discover(conn);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
	.security_changed = security_changed};

static void scan_init(void)
{
	int err;

	struct bt_scan_init_param scan_init = {
		.connect_if_match = 1,					// scan the device, and the device match the filter.
		.scan_param = NULL,						// 扫描参数为空，using the default parameters, BT_LE_SCAN_PASSIVE
		.conn_param = BT_LE_CONN_PARAM_DEFAULT};// 连接参数为空，using the default parameters, BT_LE_CONN_PARAM_DEFAULT

	bt_scan_init(&scan_init);					// 
	bt_scan_cb_register(&scan_cb);				// register the callback

	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_BAS);	// 添加过滤条件，这里只添加了UUID过滤。
	if (err)
	{
		LOG_ERR("Scanning filters cannot be set (err %d)\n", err);

		return;
	}

	err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);			// 使能过滤条件，可以有多重过滤，可以同时匹配，也可以只要匹配一个条件即可触发。
	if (err)
	{
		LOG_ERR("Filters cannot be turned on (err %d)\n", err);
	}
}

static void button_readval(void)
{
	int err;

	LOG_INF("Reading BAS value:\n");
	err = bas_read_battery_level(&bas);
	if (err)
	{
		LOG_ERR("BAS read call error: %d\n", err);
	}
}

static void button_handler(uint32_t button_state, uint32_t has_changed)
{
	uint32_t button = button_state & has_changed;

	if (button & KEY_READVAL_MASK)
	{
		button_readval();
	}
}

static void auth_cancel(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing cancelled: %s\n", addr);
}

static void pairing_complete(struct bt_conn *conn, bool bonded)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing completed: %s, bonded: %d\n", addr, bonded);
}

static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing failed conn: %s, reason %d\n", addr, reason);
}

static struct bt_conn_auth_cb conn_auth_callbacks = {
	.cancel = auth_cancel,
};

static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
	.pairing_complete = pairing_complete,
	.pairing_failed = pairing_failed};

int main(void)
{
	int err;

	LOG_INF("Starting Bluetooth Central BAS example\n");

	bas_client_init(&bas);

	err = bt_enable(NULL);
	if (err)
	{
		LOG_ERR("Bluetooth init failed (err %d)\n", err);
		return 0;
	}

	LOG_INF("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS))
	{
		settings_load();
	}

	scan_init();

	err = bt_conn_auth_cb_register(&conn_auth_callbacks);
	if (err)
	{
		LOG_ERR("Failed to register authorization callbacks.\n");
		return 0;
	}

	err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
	if (err)
	{
		LOG_ERR("Failed to register authorization info callbacks.\n");
		return 0;
	}

	err = dk_buttons_init(button_handler);
	if (err)
	{
		LOG_ERR("Failed to initialize buttons (err %d)\n", err);
		return 0;
	}

	err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
	if (err)
	{
		LOG_ERR("Scanning failed to start (err %d)\n", err);
		return 0;
	}

	LOG_INF("Scanning successfully started\n");
	return 0;
}
